#pragma once

#include <memory>
#include <map>
#include "teflon/graphics/renderable/sprite2d.h"
#include "teflon/graphics/renderable/animation2d.h"
#include "teflon/gapi/shader.h"
#include "teflon/gapi/vertex_array.h"

#include "teflon/scene/environment.h"

namespace teflon
{
#define MAX_QUADS 50000

    class SpriteBatch
    {
    private:
        SpriteVertex* vertex_base = nullptr;
        uint32_t num_vertices = 0;
        uint32_t max_vertices = 0;
        uint16_t* index_base = nullptr;
        uint32_t num_indices = 0;
        uint32_t max_indices = 0;
        
        std::map<GLuint, uint32_t> textureid2slot;
        uint32_t max_texture_slots = 16;

        GLuint vao = -1;
        GLuint vbo = -1;
        GLuint ibo = -1;

    public:
        SpriteBatch() {}
        ~SpriteBatch();

        void Init(uint32_t max_sprites);

        void Begin();
        void End();
        uint32_t NumIndices() const { return num_indices; }
        bool AddSprite(const ColoredQuad& sprite, const glm::mat4& model_transform = glm::mat4(1.0f));
        bool AddSprite(const SpriteQuad& sprite, const glm::mat4& model_transform = glm::mat4(1.0f));
        bool AddSprite(const AnimatedSprite& sprite, const glm::mat4& model_transform = glm::mat4(1.0f));

        std::map<GLuint, uint32_t> GetSlotMap() const { return textureid2slot; }
    };


    class SpriteRenderer
    {
    private:
        std::shared_ptr<ShaderProgram> program;
        SpriteBatch sprite_batch;

    public:
        SpriteRenderer();
        ~SpriteRenderer();

        void Begin(const Environment2D& env);
        void Draw(const ColoredQuad& sprite, const glm::mat4& model_transform = glm::mat4(1.0f));
        void Draw(const SpriteQuad& sprite, const glm::mat4& model_transform = glm::mat4(1.0f));
        void Draw(const AnimatedSprite& sprite, const glm::mat4& model_transform = glm::mat4(1.0f));
        void FlushBatch();
        void End();

        GLuint GetShaderProgram() const { return program->GetId(); }
    };
}
